socket filter抓包技术与开发方法

您所在的位置:网站首页 tcpdump 抓tcp包 socket filter抓包技术与开发方法

socket filter抓包技术与开发方法

#socket filter抓包技术与开发方法| 来源: 网络整理| 查看: 265

背景

eBPF是cBPF的扩展。在过去cBPF提出的时候,socket filter就是cBPF主要的使用场景,pcap/tcpdump也是使用socket filter技术实现了抓包。为了简化使用,pcap定义了自己的filter语法,便于使用,也就是使用tcpdump的时候的那些参数。pcap filter最后都会编译成cBPF,然后加载到内核中进行包过滤。

另一个广泛使用了cBPF的技术是seccomp。相比之下,现在eBPF在内核中的使用位置可是广泛太多了。

使用

为了实现抓包的功能,首先需要创建一个socket,然后将socket绑定特定网卡,最后调用setsockopt函数为这个socket配置特定选项:

创建socket

socket函数用于创建socket:

sockfd = socket(PF_PACKET, SOCK_RAW, 0);

socket传入的参数含义为:

第一个参数是协议族(protocol family),还有一个相近含义的东西是地址族(address family),现在协议族和地址族基本是等价的,内核中也是简单的宏定义替换,也就是说PF_PACKET和AF_PACKET都可以作为第一个参数,表示使用的是packet协议族 第二个参数是socket类型,SOCK_RAW表示原始套接字 第三个参数是协议,传入0表示自动选择。还可以传入htons(ETH_P_ALL)。另外还可以传入htons(ETH_P_IP),但是这样只能抓到入向的IP包。

还有一种过去采用的方法也可以使用,也就是创建了AF_INET协议族的SOCK_PACKET类型的套接字:

sockfd = socket(AF_INET,SOCK_PACKET,0)bind

接下来,调用bind指定要对哪个网卡捕获包,需要创建一个sockaddr对象,在这个对象里面指定网卡,例如指定eth0网卡可以:

struct sockaddr myaddr; memset(&myaddr, '\0', sizeof(myaddr)); myaddr.sa_family = AF_INET; strcpy(myaddr.sa_data, "eth0"); r = bind(sockfd, &myaddr, sizeof(struct sockaddr));

sockaddr中还可以指定ip地址等信息。在bind后,这个socket就和eth0网卡绑定了。

setsockopt

setsockopt用于给socket设置选项的函数,设置成功返回0,设置失败返回-1。为了设置socket filter,需要这样调用:

ret = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));

调用setsockopt的参数含义如下:

第一个参数是socket的fd 第二个参数是level,SOL_SOCKET表示socket层级,还可以使用IPPROTO_TCP表示TCP层级 第三个参数是选项名称,SO_ATTACH_FILTER表示设置的内容为cBPF过滤程序。还可以使用SO_ATTACH_BPF,表示设置eBPF过滤程序 第四个参数是设置选项的值的指针,对于SO_ATTACH_FILTER和SO_ATTACH_BPF传入的就是cBPF和eBPF程序的指令数组。第五个参数的设置选项的值的长度获取数据

在socket创建、bind、setsocketopt之后,就可以从socket读出捕获包的内容了。因为socket本身也是文件,可以直接调用读取文件的方法读取抓包的内容

性能问题

人们在使用pcap/tcpdump的过程中,发现pcap有一定的性能问题,主要表现在抓包过程中,由于处理速度的原因,包还未处理就被丢弃了。这主要是以下原因:

pcap(普通的socket filter)在处理时进行了多次数据拷贝:

第一次拷贝:数据被网卡接收,填充到内核的RX ringbuf 第二次拷贝:包从RX ringbuf拷贝到sk_buff/skb结构体内 第三次拷贝:由于socket filter自身不能影响原始数据包,包括不能修改数据包的内容,也不能阻塞数据包的处理,所以对skb进行了一次拷贝,将拷贝的内容发送给自己的socket处理,原始的数据进行正常的逻辑 第四次拷贝:用户从内核中读取socket的内容,包经历了一次内核空间到用户空间的数据拷贝

前两次是内核的行为,任何网络包都会进行,此时也没有到socket filter的处理位置。对于收包来说,socket filter的处理位置在skb被创建后,在tc之前。

使用pcap时,每次读取一个包都要进行一次系统调用,执行第四次拷贝,如果要获取包的时间戳,需要两次系统调用。另一方面,pcap的缓冲区很小(默认为2MB),每次缓冲区填满新的数据包就会被丢弃,即使设置很大的缓冲区,丢包问题也不能缓解,甚至可能更加严重。怀疑在处理缓冲区的数据的过程中引入的耗时导致新到的数据包不能被填充到缓冲区。

PACKET MMAP优化

为了解决原始的socket filter产生的性能问题,有人针对原来的socket filter进行优化,即PACKET MMAP,具体优化内容为:

使用MMAP映射内核空间内存和用户空间内存,免去最后一次内存拷贝MMAP的内存空间以ring buffer形式使用,每次需要从内存读取包的内容时,使用系统的poll方法等待内核复制的网络包,当多个包在ring buffer中,也可以多个包一起传递到用户空间

PACKET MMAP的使用是在原有的socket filter基础上添加一个socket option:

setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req))

req的定义如下

struct tpacket_req { unsigned int tp_block_size; unsigned int tp_block_nr; unsigned int tp_frame_size; unsigned int tp_frame_nr; };

tpacket_req各字段含义为:

tp_block_size:单个block的大小 tcp_block_nr:block数量 tp_frame_size:frame的大小 tp_frame_nr:frame的数量

block表示一块连续的内存空间,一个block里面有多个frame(tp_block_size/tp_frame_size个数)

tpacket_req还有v3版本,拥有更多的配置参数

注意,使用PF_PACKET不仅仅可以进行原始包的捕获,也可以发送原始包,所以针对发送也有类似的设置

setsockopt(fd, SOL_PACKET, PACKET_TX_RING, (void *) &req, sizeof(req))

从socket读取数据,也要使用mmap的方式:

rx_ring = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

具体实现可以参照内核文档:https://docs.kernel.org/networking/packet_mmap.html

PF_RING

PF_RING进行了进一步的优化,相比于PACKET MMAP,PF_RING消除了第二次拷贝,即从网卡TX ringbuf到skb的拷贝,直接对TX ringbuf进行MMAP

PF_RING还有更进一步的模式,即ZC(zero-copy)的PF_RING,通过配置自己的网卡驱动,在网卡数据还没有到达TX ringbuf时,使用DNA(Direct NIC Access 直接网卡访问)技术,将驱动的内存空间映射到用户的内存空间。注意这种模式下,原有的网络包都被PF_RING接管而不会进入内核网络栈

使用golang进行开发

golang下有一个很好用的抓包库gopacket,使用cgo对上述的三种模式进行了封装,但是不支持eBPF(支持加载eBPF程序,但是不支持eBPF开发),如果需要编写eBPF socket filter,可以考虑cilium的ebpf或libbpfgo这两个eBPF开发框架,再用gopacket进行处理。

gopacket: https://github.com/google/gopacket cilium/ebpf: https://github.com/cilium/ebpf libbpfgo: https://github.com/aquasecurity/libbpfgo

还可以直接通过golang的syscall或unix库调用SetsockoptInt和SetsockoptTpacketTeq方法。使用这种方法时,需要自行生成对应的cBPF或eBPF程序

手动编写cBPF汇编并且生成指令可以通过bpf_asm工具:https://github.com/cloudflare/bpftools/tree/master/linux_tools



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3